[ORG 0x500]

%include "oskernel/include/global.inc"

[SECTION .text]
[BITS 16]
jmp setUp_Start

CODE_SEG_START equ 0x00000
DATA_SEG_START equ 0x00000
CODE_SEG_LIMIT equ 0xfffff
DATA_SEG_LIMIT equ 0xfffff

CODE_SELECTOR equ (1 << 3)
DATA_SELECTOR equ (2 << 3)

[SECTION .data]
ARDS_TIMES_BUFFER equ 0x7e00
ARDS_BUFFER equ 0x7e02
ARDS_TIMES dw 0

CHECK_BUFFER_OFFSET dw 0

[SECTION .gdt]
gdt_start:
    dq 0
code_gdt_start:
    dw CODE_SEG_LIMIT & 0xffff
    dw CODE_SEG_START & 0xffff
    db CODE_SEG_START >> 16 & 0xff
    ;    P_DPL_S_TYPE
    db 0b1_00_1_1000
    ;    G_DB_AVL_LIMIT
    db 0b0_1_00_0000 | (CODE_SEG_LIMIT >> 16 & 0xf)
    db CODE_SEG_START >> 24 & 0xff

data_gdt_start:
    dw DATA_SEG_LIMIT & 0xffff
    dw DATA_SEG_START & 0xffff
    db DATA_SEG_START >> 16 & 0xff
    db 0b1_00_1_0010
    db 0b1_1_00_0000 | (DATA_SEG_LIMIT >> 16 & 0xf)
    db DATA_SEG_START >> 24 & 0xff

gdt_ptr:
    dw $ - gdt_start
    dd gdt_start



[SECTION .text]
[BITS 16]
setUp_Start:
    mov ax, 0
    mov ds, ax
    mov es, ax
    mov fs, ax
    mov gs, ax

    mov si, prepare_enter_protected_mode_msg
    call print

    call memory_check

.enter_protected_mode:
    cli

    lgdt [gdt_ptr]

    in al, 92h
    or al, 0b00000010
    out 92h, al

    mov eax, cr0
    or eax, 1
    mov cr0, eax

    jmp CODE_SELECTOR:protected_mode

print:
    mov ah, 0x0e
    mov bh, 0
    mov bl, 0x01
.loop:
    mov al, [si]
    cmp al, 0
    je .done
    int 0x10

    inc si
    jmp .loop

.done:
    ret

memory_check:
    xor ebx, ebx
    mov di, ARDS_BUFFER

.loop:
    mov eax, 0xe820
    mov ecx, 20
    mov edx, 0x534D4150
    int 0x15

    jc .memory_check_error

    add di, cx
    inc dword [ARDS_TIMES]

    cmp ebx, 0
    jne .loop

    mov ax, word [ARDS_TIMES]
    mov word [ARDS_TIMES_BUFFER], ax

    mov [CHECK_BUFFER_OFFSET], di

.memory_check_success:
    mov si, memory_check_success_msg
    call print
    ret

.memory_check_error:
    mov si, memory_check_error_msg
    call print

    ; 如果内存检测出错，直接死循环
    jmp $

[BITS 32]
protected_mode:
    mov ax, DATA_SELECTOR
    mov ds, ax
    mov es, ax
    mov ss, ax
    mov fs, ax
    mov gs, ax
    
    mov esp, REAL_MODE_STACK_BASE

    mov edi, KERNEL_ADDR_BASE
    mov ecx, 3
    ; 这里和老师的不一样，为了能在Ubuntu高版本能运行，加大了32位kernel占用的扇区数
    mov bl, 40
    call read_hd

    ; 接下来load64位内核
    ; mov edi, KERNEL_64_ADDR_BASE
    ; mov ecx, 47
    ; mov bl, 20
    ; call read_hd

    ; 将AP初始化程序(即ap_init.asm编译后的程序)读入内存
    mov edi, 0xf000
    mov ecx, 43
    mov bl, 4
    call read_hd

    ; 64位内核太大了，专门提一个函数来加载
    call load_x64_kernel
    
    jmp CODE_SELECTOR:KERNEL_ADDR_BASE

read_hd:
    mov dx, 0x1f2
    mov al, bl
    out dx, al

    mov dx, 0x1f3
    mov al, cl
    out dx, al

    mov dx, 0x1f4
    mov al, ch
    out dx, al

    shr ecx, 16
    mov dx, 0x1f5
    mov al, cl
    out dx, al

    mov dx, 0x1f6
    mov al, ch
    and al, 0b1111
    or al, 0b1110_0000
    out dx, al

    mov dx, 0x1f7
    mov al, 0x20
    out dx, al

    xor cx, cx
    mov cl, bl

.start_read:
    push cx

    call wait_hd_prepare
    call read_hd_data

    pop cx
    loop .start_read
.return:
    ret

wait_hd_prepare:
    mov dx, 0x1f7
.check:
    in al, dx
    and al, 0b1000_1000
    cmp al, 0b0000_1000
    jnz .check

    ret

read_hd_data:
    mov dx, 0x1f0
    mov cx, 256
.read_word:
    in ax, dx
    mov [edi], ax
    add edi, 2
    loop .read_word

    ret

load_x64_kernel:
    xor esi, esi
.loop_load_x64_kernel:
    ; 256 * 512 = 2^8 * 2^9 = 2^17 = 0x20000
    ; mov eax, 0x20000
    ; 对于上面的这个我始终是保持疑问的.我认为应该是下面的这个才对,但是目前无法验证
    mov eax, 0xff * 0x200
    mul esi
    lea edi, [KERNEL_64_ADDR_BASE + eax]

    mov eax, 0xff
    mul esi
    lea ecx, [eax + KERNEL_64_SECTOR_START]
    mov bl, 0xff

    push esi
    call read_hd

    pop esi

    inc esi
    cmp esi, KERNEL_64_LOAD_LOOP_COUNT
    jl .loop_load_x64_kernel
    ret

prepare_enter_protected_mode_msg:
    db "prepare to enter to protected mode...", 10, 13, 0

memory_check_success_msg:
    db "memory check success...", 10, 13, 0

memory_check_error_msg:
    db "memory check error...", 10, 13, 0
